package com.instpower.things.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.instpower.things.config.Configs;
import com.instpower.things.config.OtaStatus;
import com.instpower.things.entity.FirmwareInfo;
import com.instpower.things.helper.Checksum;
import com.instpower.things.helper.DateUtil;
import com.instpower.things.instpower.Connection;
import com.instpower.things.instpower.Context;
import com.instpower.things.instpower.DeviceSession;
import com.instpower.things.instpower.DeviceSessionManager;
import com.instpower.things.transport.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.nio.ByteBuffer;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @ClassName OtaHandler
 * @Author: hellmash@gmail.com
 * @Date: 2020/5/8
 * @Version: 1.0.0
 * @Description: OTA升级指令
 */
public class OtaHandler implements MessageListener {
    private final static Logger logger = LoggerFactory.getLogger("CORE");

    private static final ObjectMapper objectMapper = new ObjectMapper();
    /**
     * 每批次处理间隔
     */
    private final static int OTA_DELAY_MINUTES = 10 * 60 * 1000;
    /**
     * 批次抓取数量
     */
    private final static int OTA_FETCH_LIMIT = 50;
    @Value("${things.timeZone}")
    private String zoneId;
    @Autowired
    private InstService instService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public void onMessage(Message message) {
        try {
            JsonNode msg = objectMapper.readTree(message.getBody());

            Integer taskId = msg.get("taskId").asInt();
            logger.debug("OTA TASKID:::()" + taskId);

            if (taskId == null || taskId < 1)
                return;

            Map<String, Object> task = instService.findTaskByKey(taskId);
            if (!(OtaStatus.CREATE.Val().equals(task.get("ota_status"))))
                return;
            //通过OTA任务获取升级固件信息
            FirmwareInfo firmwareInfo = this.getFirmwareInfo(task);
            //单机实时更新
            if ("single_update".equals(task.get("ota_type"))) {
                String sn = (String) task.get("cabinet_sn");
                //发送升级信息
                this.sendFirmware(sn, firmwareInfo);
                instService.updateTask(taskId, OtaStatus.EXECUTE);
                redisTemplate.opsForHash().put(Configs.OTA_SINGLE_KEY, sn, Integer.toString(taskId));
                logger.debug("REDIS缓存TASKID成功!升级机柜号:{}", sn);
            } else if ("batch_update".equals(task.get("ota_type"))) {
                //指令处理结束时间
                String end_time = task.get("end_time").toString();
                Date endTime = DateUtil.parseDateTime(end_time);
                String firmwareVersion = task.get("ota_version").toString();

                //发送批量OTA升级
                ThreadPoolTaskExecutor taskExecutor = Context.getBean("taskExecutor");
                taskExecutor.execute(() -> {

                    int otaDelayMinutes = OTA_DELAY_MINUTES;
                    int otaFetchLimit = OTA_FETCH_LIMIT;

                    //查找数据表sys_system获取这两项数据参数
                    List<Map<String, Object>> otaConfigs = instService.findOtaConfig();
                    if (otaConfigs != null) {
                        for (Map<String, Object> otaConfig : otaConfigs) {
                            if (otaConfig.get("key_name") != null) {
                                if ("ota_delay_minutes".equals(otaConfig.get("key_name").toString())) {
                                    if (otaConfig.get("key_value") != null) {
                                        otaDelayMinutes = Integer.parseInt(otaConfig.get("key_value").toString()) * 60000;
                                    }
                                } else if ("ota_fetch_limit".equals(otaConfig.get("key_name").toString())) {
                                    if (otaConfig.get("key_value") != null) {
                                        otaFetchLimit = Integer.parseInt(otaConfig.get("key_value").toString());
                                    }
                                }
                            }
                        }
                    }

                    instService.updateTask(taskId, OtaStatus.EXECUTE);
                    DeviceSession session;
                    int startId = 0;

                    for (; ; ) {
                        //更新时间过期,设置未任务完成
                        LocalDateTime now = LocalDateTime.now();
                        if (endTime.before(Date.from(now.atZone(ZoneId.of(zoneId)).toInstant()))) {
                            instService.updateCabinetOtaStatus(taskId, OtaStatus.FINISH);
                            return;
                        }
                        //查询数据库中从id后面的第100个待升级的设备
                        List<Map<String, Object>> cabinets = instService.findCabinet(startId, firmwareVersion, otaFetchLimit);
                        if (cabinets == null || cabinets.size() < 1) {
                            instService.updateCabinetOtaStatus(taskId, OtaStatus.FINISH);
                            return;
                        }
                        logger.debug("本批次更新数量{}", cabinets.size());
                        //获取当前所有
                        for (Map<String, Object> cabinet : cabinets) {
                            Boolean networkStatus = (Boolean) cabinet.get("network_status");
                            logger.debug("更新设备SN:{},该机器network_status:{}", cabinet.get("cabinet_sn"), cabinet.get("network_status"));
                            //设置最后一个处理
                            startId = Integer.parseInt(cabinet.get("id").toString());
                            if (networkStatus) {
                                this.sendFirmware(cabinet.get("cabinet_sn").toString(), firmwareInfo);
                            }
                        }
                        try {
                            Thread.sleep(otaDelayMinutes);
                        } catch (InterruptedException e) {
                            logger.debug("线程中断失败，请重启系统!");
                            logger.error(e.getLocalizedMessage(), e);
                        }
                    }
                });
            }
        } catch (Exception e) {
            logger.error("下发升级指令异常");
            logger.error(e.getLocalizedMessage(), e);
        }
    }

    /**
     * 像机柜发送固件信息
     *
     * @param sn
     * @param firmwareInfo
     */
    private void sendFirmware(String sn, FirmwareInfo firmwareInfo) {
        Response resp = new Response();
        Header header = new Header();
        header.setPtrol((byte) 0x02);
        header.setAddr(sn);
        header.setCommand((short) 0x0F05);
        MTData data = new MTData();
        DataBuffer dataBuffer = null;
        dataBuffer = firmwareInfo.encode();
        int dataLength = dataBuffer.readableBytes();
        data.setDatas(dataBuffer.readBytes(dataLength));
        //release after read
        dataBuffer.release();
        dataLength += 2;
        header.setLength((short) dataLength);
        resp.setHeader(header);
        resp.setData(data);

        DataBuffer buffer = resp.encode();
        int checksum = Checksum.crc16(Checksum.CRC16_ANKER,
                ByteBuffer.wrap(buffer.readByteArray(buffer.readableBytes())));
        //release after read
        buffer.release();

        Ender ender = new Ender();
        ender.setChecksum(checksum);
        resp.setEnder(ender);

        DeviceSession session;
        session = DeviceSessionManager.getInstance().get(sn);
        if (null != session) {
            Connection connection = session.getMConnection();
            connection.sendResponse(resp);
        }
    }

    private void doEndResponse(){

    }

    /**
     * 通过任务详情，生成固件信息
     *
     * @param task
     * @return
     */
    private FirmwareInfo getFirmwareInfo(Map<String, Object> task) {
        FirmwareInfo firmwareInfo = new FirmwareInfo();
        String otaUrl = (String) task.get("ota_url");
        if ("http".equals(otaUrl.substring(0, otaUrl.indexOf(":")))) {
            firmwareInfo.setProtocol(FirmwareInfo.Protocol.HTTP);
            String ipport = otaUrl.substring(otaUrl.indexOf("//") + 2, otaUrl.indexOf("/", 7));
            int index = ipport.indexOf(":");
            if (index > 0) {
                firmwareInfo.setIpAddress(ipport.substring(0, ipport.indexOf(":")));
                firmwareInfo.setPort(Short.parseShort(ipport.substring(index + 1)));
            } else {
                firmwareInfo.setIpAddress(ipport);
                firmwareInfo.setPort((short) 80);
            }
        } else {
            firmwareInfo.setProtocol(FirmwareInfo.Protocol.HTTPS);
            firmwareInfo.setIpAddress("0.0.0.0");
            firmwareInfo.setPort((short) 443);
        }
        firmwareInfo.setUsername("");
        firmwareInfo.setPassword("");
        firmwareInfo.setUrl(otaUrl);
        firmwareInfo.setFileLenght((int) task.get("file_length"));
        firmwareInfo.setCheckSum((int) task.get("check_sum"));

        return firmwareInfo;
    }
}
